use std::fmt;
use std::io::{Read,Result};
use std::ffi::{CString,CStr};
use std::path::Path;

use super::ukey::{Sha1str, Ukey};
use super::umutex::Umutex;
use super::mbox::{MBox};
use super::meta::{Meta};

#[repr(C)]
#[derive(Debug,Default)]
pub struct head {
    name: [u8; 32],    //char name[32];
    mem_t: [u8; 32],   //char type[32];
    pub meta_size: u64,    //long long metaSize;
    pub data_size: u64,    //long long dataSize;
    pub cp_size: u64,      //long long cpSize;
    longitude: f64,    //double longitude;
    latitude: f64,     //double latitude;
    altitude: f64,     //double altitude;
    version: Sha1str,  //struct sha1str version;
    signture: Sha1str, //struct sha1str sha1;
    g_mutex: Umutex,   //umutex g_mutex;
}

impl fmt::Display for head {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = head::u8_to_string(&self.name);
        let mem_t = head::u8_to_string(&self.mem_t);
            
        write!(
            f,
            "{:?}:{:?},size(m:{},d:{},c:{}),pos(NS:{},EW:{},H:{}),v:{},s:{},lock:{}",
            name,
            mem_t,
            self.meta_size,
            self.data_size,
            self.cp_size,
            self.longitude,
            self.latitude,
            self.altitude,
            self.version,
            self.signture,
            self.g_mutex.value()
        )
    }
}

impl head {
    pub fn read_head(fname:&Path) -> Result<head> {
        let mut buf = [0u8;std::mem::size_of::<head>()];
        let mut file = std::fs::File::open(fname.clone())?;
        file.read(&mut buf)?;
        let mut h = head::default();

        let config_size = std::mem::size_of::<head>();
        unsafe {
            let config_slice = std::slice::from_raw_parts_mut(&mut h as *mut _ as *mut u8, config_size);
            let mut b = &buf[..];
            b.read_exact(config_slice)?;
        }
        Ok(h)
    }
    pub fn check(&self,fname: String) -> Option<Ukey> {
        let buf = unsafe {
            std::slice::from_raw_parts(
            (self as *const head) as *const u8,
            ::std::mem::size_of::<head>(),
        )};
        let head_static_len = offset_of!(head,signture);
        let s1 = Ukey::signture(buf, head_static_len as usize);
        if s1 != self.signture {
            None
        } else {
            let kid = s1.key();
            let strid = kid.to_string();
            if fname.contains(&strid) {
                Some(kid)
            } else {
                None
            }
        }
    }
    pub fn attach(m:&Meta) -> Result<MBox<head>> {
        m.attach::<head>(0 as usize)
    }
    fn u8_to_string(c: &[u8]) -> CString {
        let rust_id = unsafe { CStr::from_ptr(c.as_ptr() as *const i8) };
        rust_id.to_owned()
    }
    pub fn get_name(&self) -> CString {
        head::u8_to_string(&self.name).clone()
    }
}

pub trait Headed {
    type ehead : fmt::Debug;
}

#[repr(C)]
#[derive(Debug)]
pub struct AHead<T> where T:Headed {
    head:head,
    aon:T::ehead
}

impl<T> AHead<T> where T:Headed {
    pub fn as_head(m : &MBox<AHead<T>>) -> Result<MBox<head>> {m.as_type::<head>()}
    pub fn addon<E>(m : &MBox<AHead<T>>) -> Result<MBox<E>> {
        let o = offset_of!(AHead<T>,aon);
        m.as_subtype::<E>(o)
    }
}